package com.uziot.bucket.async.pool;

import com.uziot.bucket.async.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author shidt
 * @version V1.0
 * @className ThreadPoolConfig
 * @date 2020-12-30 22:43:30
 * @description 线程池配置
 * 1、springboot使用线程池有两种方式，第一种就是使用此类的方式，构造线程
 * 池，调用的时候标注@async(指定线程池)
 * 2、第二种方式是重写 spring 默认线程池，使用这种方式的好处是可以直接使用 @Async 注解
 * 创建配置类AsyncTaskExecutePool1 并且实现AsyncConfigurer 类
 * <p>
 * 注意
 * 同类中调用带有@Async 的方法是不生效的
 * 例子中的参数根据具体的需求修改
 */

@EnableAsync
@Configuration
public class ThreadPoolConfig {
    /**
     * 核心线程池大小
     */
    private final int corePoolSize = 50;

    /**
     * 最大可创建的线程数
     */

    private final int maxPoolSize = 200;

    /**
     * 队列最大长度
     */
    private final int queueCapacity = 1000;

    /**
     * 线程池维护线程所允许的空闲时间
     */
    private final int keepAliveSeconds = 30;

    /**
     * 1. corePoolSize：指定了线程池中的线程数量。
     * 2. maximumPoolSize：指定了线程池中的最大线程数量。
     * 3. keepAliveTime：当前线程池数量超过 corePoolSize 时，多余的空闲线程的存活时间，即多
     * 次时间内会被销毁。
     * 4. unit：keepAliveTime 的单位。
     * 5. workQueue：任务队列，被提交但尚未被执行的任务。
     * 6. threadFactory：线程工厂，用于创建线程，一般用默认的即可。
     * 7. handler：拒绝策略，当任务太多来不及处理，如何拒绝任务。
     * 线程池1
     * 线程池中的线程已经用完了，无法继续为新任务服务，同时，等待队列也已经排满了，再也
     * 塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。
     * JDK 内置的拒绝策略如下：
     * 1. AbortPolicy ： 直接抛出异常，阻止系统正常运行。
     * 2. CallerRunsPolicy ： 只要线程池未关闭，该策略直接在调用者线程中，运行当前被丢弃的
     * 任务。显然这样做不会真的丢弃任务，但是，任务提交线程的性能极有可能会急剧下降。
     * 3. DiscardOldestPolicy ： 丢弃最老的一个请求，也就是即将被执行的一个任务，并尝试再
     * 次提交当前任务。
     * 4. DiscardPolicy ： 该策略默默地丢弃无法处理的任务，不予任何处理。如果允许任务丢
     * 失，这是最好的一种方案。
     * 以上内置拒绝策略均实现了 RejectedExecutionHandler 接口，若以上策略仍无法满足实际
     * 需要，完全可以自己扩展 RejectedExecutionHandler 接口。
     * <p>
     * Java 线程池工作过程
     * 1. 线程池刚创建时，里面没有一个线程。任务队列是作为参数传进来的。不过，就算队列里面
     * 有任务，线程池也不会马上执行它们。
     * 2. 当调用 execute() 方法添加一个任务时，线程池会做如下判断：
     * a) 如果正在运行的线程数量小于 corePoolSize，那么马上创建线程运行这个任务；
     * b) 如果正在运行的线程数量大于或等于 corePoolSize，那么将这个任务放入队列；
     * c) 如果这时候队列满了，而且正在运行的线程数量小于 maximumPoolSize，那么还是要
     * 创建非核心线程立刻运行这个任务；
     * d) 如果队列满了，而且正在运行的线程数量大于或等于 maximumPoolSize，那么线程池
     * 会抛出异常 RejectExecutionException。
     * 3. 当一个线程完成任务时，它会从队列中取下一个任务来执行。
     * 4. 当一个线程无事可做，超过一定的时间（keepAliveTime）时，线程池会判断，如果当前运
     * 行的线程数大于 corePoolSize，那么这个线程就被停掉。所以线程池的所有任务完成后，它
     * 最终会收缩到 corePoolSize 的大小。
     *
     * @return ThreadPoolTaskExecutor
     */
    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(corePoolSize);
        // 设置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        // 设置队列容量
        executor.setQueueCapacity(queueCapacity);
        // 设置线程活跃时间（秒）
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置默认线程名称
        executor.setThreadNamePrefix("Exec-1-");
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

    /**
     * 线程池2
     *
     * @return ThreadPoolTaskExecutor
     */
    @Bean(name = "threadPoolTaskExecutor2")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor2() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(corePoolSize);
        // 设置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        // 设置队列容量
        executor.setQueueCapacity(queueCapacity);
        // 设置线程活跃时间（秒）
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置默认线程名称
        executor.setThreadNamePrefix("Exec-2-");
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

    /**
     * 执行周期性或定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(corePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
    }

    public int getCorePoolSize() {
        return corePoolSize;
    }

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public int getQueueCapacity() {
        return queueCapacity;
    }

    public int getKeepAliveSeconds() {
        return keepAliveSeconds;
    }
}
